﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;

#if WIN32
using DWORD_PTR = System.UInt32;
#elif WIN64
using DWORD_PTR = System.UInt64;
#endif

namespace Patcher
{
    public partial class frmMain : Form
    {

        private Dictionary<string, Process> ProcessList = new Dictionary<string, Process>();

        #region Form events
        public frmMain()
        {
            InitializeComponent();
        }

        private void frmMain_Load( object sender, EventArgs e )
        {
            Globals.CheckUpdatesForm.CheckUpdates();
            Globals.CheckUpdatesForm.ShowDialog( this );

            RefreshProcessList();
        }

        private void menuOptions_File_Exit_Click( object sender, EventArgs e )
        {
            Application.Exit();
        }

        private void menuOptions_Tools_Patch_Click( object sender, EventArgs e )
        {
            PatchCurrentProcess();
        }

        private void menuOptions_Tools_Unpatch_Click( object sender, EventArgs e )
        {
            UnpatchCurrentProcess();
        }

        private void menuOptions_Tools_Refresh_Click( object sender, EventArgs e )
        {
            RefreshProcessList();
        }

        private void menuOptions_Help_CheckUpdates_Click( object sender, EventArgs e )
        {
            Globals.CheckUpdatesForm.ResetDownloadQueue();
            Globals.CheckUpdatesForm.CheckUpdates();
            Globals.CheckUpdatesForm.ShowDialog( this );
        }

        private void menuOptions_Help_About_Click( object sender, EventArgs e )
        {
            Globals.AboutForm.ShowDialog( this );
        }

        private void lstProcesses_DoubleClick( object sender, EventArgs e )
        {
            TogglePatchCurrentProcess();
        }
        #endregion

        /// <summary>
        /// Refreshes the process list.
        /// </summary>
        private void RefreshProcessList()
        {
            ProcessList.Clear();
            lstProcesses.Items.Clear();

            foreach( Process pProcess in Process.GetProcessesByName( "Client" ) )
            {
                string sPID = pProcess.Id.ToString();
                ProcessList.Add( sPID, pProcess );

                string sMD5Hash = Functions.MD5( Functions.GetMainModulePath( pProcess ) );
                string sGameVersion = "Unknown";
                string sGamePlatform = "";

                bool bIsPatched = false;

                if( Globals.ClientHashes.ContainsKey( sMD5Hash ) )
                {
                    sGameVersion = Globals.ClientHashes[sMD5Hash][0];
                    sGamePlatform = Globals.ClientHashes[sMD5Hash][1];

                    if( sGamePlatform == Globals.PlatformString )
                    {
                        IntPtr hProcess = Memory.OpenProcess( pProcess.Id );
                    
                        DWORD_PTR dwBaseAddress = (DWORD_PTR) pProcess.MainModule.BaseAddress;
                        DWORD_PTR dwWatermarkHandle = Memory.Read<DWORD_PTR>( hProcess, dwBaseAddress + Offsets.Patches.WatermarkHandle );
                        bIsPatched = Memory.Read<DWORD_PTR>( hProcess, dwWatermarkHandle + Offsets.Patches.WatermarkEnable ) == 0;

                        Memory.CloseHandle( hProcess );
                    }
                }

                ListViewItem pListItem = lstProcesses.Items.Add( sPID );
                pListItem.SubItems.Add( sGameVersion );
                pListItem.SubItems.Add( sGamePlatform );
                pListItem.SubItems.Add( bIsPatched ? "Yes" : "No" );
            }
        }

        /// <summary>
        /// Patches or unpatches the selected process.
        /// </summary>
        private void TogglePatchCurrentProcess()
        {
            if( lstProcesses.SelectedItems.Count != 1 )
                return;

            if( lstProcesses.SelectedItems[0].SubItems[3].Text == "No" )
                PatchCurrentProcess();
            //else
            //    UnpatchCurrentProcess();
        }

        /// <summary>
        /// Patches the selected process.
        /// </summary>
        private void PatchCurrentProcess()
        {
            if( lstProcesses.SelectedItems.Count != 1 || lstProcesses.SelectedItems[0].SubItems[3].Text != "No" )
                return;

            if( lstProcesses.SelectedItems[0].SubItems[1].Text != Globals.GameVersion )
            {
                MessageBox.Show( "This game version is not yet supported! Please check for updates periodically.",
                                    "Unsupported version", MessageBoxButtons.OK, MessageBoxIcon.Error );
                return;
            }
            
            if( lstProcesses.SelectedItems[0].SubItems[2].Text != Globals.PlatformString )
            {
                MessageBox.Show( "You are running the " + Globals.PlatformString + " version of WildStar Watermark Patcher. " +
                                 "Please compile or download the version corresponding to this platform.",
                                    "Unsupported version", MessageBoxButtons.OK, MessageBoxIcon.Error );
                return;
            }

            if( PatchProcess( ProcessList[lstProcesses.SelectedItems[0].Text] ) )
                lstProcesses.SelectedItems[0].SubItems[3].Text = "Yes";
        }

        /// <summary>
        /// Unpatches the selected process.
        /// </summary>
        private void UnpatchCurrentProcess()
        {
            if( lstProcesses.SelectedItems.Count != 1 || lstProcesses.SelectedItems[0].SubItems[3].Text != "Yes" )
                return;

            if( UnpatchProcess( ProcessList[lstProcesses.SelectedItems[0].Text] ) )
                lstProcesses.SelectedItems[0].SubItems[3].Text = "No";
        }

        /// <summary>
        /// Patches a specified process.
        /// </summary>
        private bool PatchProcess( Process pProcess )
        {
            IntPtr hProcess = Memory.OpenProcess( pProcess.Id );
            if( hProcess == IntPtr.Zero )
                return false;

            DWORD_PTR dwBaseAddress = (DWORD_PTR) pProcess.MainModule.BaseAddress;
            DWORD_PTR dwWatermarkHandle = Memory.Read<DWORD_PTR>( hProcess, dwBaseAddress + Offsets.Patches.WatermarkHandle );
            bool bResult = Memory.Write<DWORD_PTR>( hProcess, dwWatermarkHandle + Offsets.Patches.WatermarkEnable, 0 );
            Memory.CloseHandle( hProcess );

            if( bResult )
                Functions.PlaySound( "Resources\\Success.wav" );
            else
                Functions.PlaySound( "Resources\\Error.wav" );

            return bResult;
        }

        /// <summary>
        /// Unpatches a specified process.
        /// </summary>
        private bool UnpatchProcess( Process pProcess )
        {
            IntPtr hProcess = Memory.OpenProcess( pProcess.Id );
            if( hProcess == IntPtr.Zero )
                return false;

            bool bResult = false; // TODO: Unpatch.
            Memory.CloseHandle( hProcess );

            if( bResult )
                Functions.PlaySound( "Resources\\Success.wav" );
            else
                Functions.PlaySound( "Resources\\Error.wav" );

            return bResult;
        }

    }
}
